#include <sys/types.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <netdb.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <netinet/in.h>

#define BUFF_SIZE 1024
#define TIME_OUT 6000000

//客户端认证请求
typedef struct client_license_request
{

    char ver;
    char nmethods;
    char methods[255];

}client_license_request;


//服务端回应认证
typedef struct server_license_response
{
    char ver;
    char method;
}server_license_response;

//客户端连接请求
typedef struct client_connect_request
{
    char ver;
    char cmd;
    char rsv;
    char type;
    char addr[4]; //目的服务器ip-这里配置127.0.0.1
    char port[2]; //目的服务器端口-这里配置端口1080


}client_connect_request;


//服务端回应连接
typedef struct server_connect_response
{
    char ver;
    char rep;
    char rsv;
    char type;
    char addr[4];
    char port[2];
}server_connect_response;

int socketfd_tcp;

int ForwardData(int sock, int real_server_sock)
{
    char recv_buffer[BUFF_SIZE] = { 0 };
    fd_set fd_read;
    struct timeval time_out;
    time_out.tv_sec = 0;
    time_out.tv_usec = TIME_OUT;
    int ret = 0;
    printf("线程%u进行数据转发\n", (int)pthread_self());
    while (1)
    {
        FD_ZERO(&fd_read);
        FD_SET(sock, &fd_read);
        FD_SET(real_server_sock, &fd_read);
        ret = select((sock > real_server_sock ? sock : real_server_sock) + 1, &fd_read, NULL, NULL, &time_out);
        if (-1 == ret)
        {
            break;
        }
        else if (0 == ret)
        {
            continue;
        }
        if (FD_ISSET(sock, &fd_read))
        {
            memset(recv_buffer, 0, BUFF_SIZE);
            ret = recv(sock, recv_buffer, BUFF_SIZE, 0);
            if (ret > 0)
            {
                ret = send(real_server_sock, recv_buffer, ret, 0);
                if (ret == -1)
                {
                    break;
                }
            }
            else if (ret == 0)
            {
                break;
            }
            else
            {
                break;
            }
        }
        else if (FD_ISSET(real_server_sock, &fd_read))
        {
            memset(recv_buffer, 0, BUFF_SIZE);
            ret = recv(real_server_sock, recv_buffer, BUFF_SIZE, 0);
            if (ret > 0)
            {
                ret = send(sock, recv_buffer, ret, 0);
                if (ret == -1)
                {
                    break;
                }
            }
            else if (ret == 0)
            {
                break;
            }
            else
            {
                break;
            }
        }
    }
    return 0;
}

void tcp_creat()
{
    socketfd_tcp = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd_tcp == -1)
    {
        perror("socketfd_tcp");
        exit(-1);
    }

    struct sockaddr_in addr_tcp;
    bzero(&addr_tcp, sizeof(addr_tcp));

    addr_tcp.sin_family = AF_INET;
    addr_tcp.sin_port = htons(1080);
    addr_tcp.sin_addr.s_addr = INADDR_ANY;

    int re = bind(socketfd_tcp, (struct sockaddr*)&addr_tcp, sizeof(addr_tcp));
    if (re == -1)
    {
        perror("bind");
        exit(-1);
    }

    re = listen(socketfd_tcp, 100);
    if (re == -1)
    {
        perror("listen");
        exit(-1);
    }

}

//代理服务器连接目的服务器
int connect_dest_server(client_connect_request* connect_request)
{

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        perror("socketfd_tcp");
        return -1;
    }
    struct sockaddr_in sin_server;
    bzero(&sin_server, sizeof(sin_server));
    sin_server.sin_family = AF_INET;
    memcpy(&sin_server.sin_addr, &connect_request->addr, sizeof(connect_request->addr));
    memcpy(&sin_server.sin_port, &connect_request->port, sizeof(connect_request->port));

    int re = connect(fd, (struct sockaddr*)&sin_server, sizeof(sin_server));
    if (re == -1)
    {
        return -1;
    }
    printf("目的服务器连接成功\n");
    return fd;
}

int sock5_license(struct sockaddr_in* addr_client, socklen_t len, int fd)
{
    char buffer[256] = { 0 };
    read(fd, buffer, sizeof(buffer));
    client_license_request* license_request = (client_license_request*)buffer;

    printf("客户端版本%d\n",license_request->ver);
    if (license_request->ver != 0x5)
    {
        return 0;
    }
    printf("客户认证信息通过，回应认证信息\n");

    server_license_response license_response;
    license_response.ver = 0x5;
    license_response.method = 0x0;
    char buff[2] = { 0 };
    memcpy(buff, &license_response, sizeof(buff));

    write(fd, buff, sizeof(buff));

    printf("已发送回应请求\n");

    //接收连接请求
    bzero(&buffer, sizeof(buffer));
    printf("等待接收客户连接请求\n");
    read(fd, buffer, sizeof(buffer));
    client_connect_request* connect_request = (client_connect_request*)buffer;


    //认证连接请求失败（类型、版本等）
    if (connect_request->ver != 0x5)
    {
        return 0;
    }
    if (connect_request->cmd != 0x1)
    {
        return 0;
    }
    if (connect_request->type != 0x01)
    {
        return 0;
    }

    int dest_fd = connect_dest_server(connect_request);
    if (dest_fd == -1)
    {
        return -1;
    }

    char buffer1[10] = { 0 };
    bzero(&buffer, sizeof(buffer1));


    server_connect_response connect_response;
    bzero(&connect_response, sizeof(connect_response));
    connect_response.ver = 0x5;
    connect_response.rep = 0x00;
    connect_response.rsv = 0x00;
    connect_response.type = 0x01;

    memcpy(buffer1, &connect_response, sizeof(connect_response));
    write(fd, buffer1, sizeof(buffer1));

    printf("已发送回应请求\n");
    ForwardData(fd, dest_fd);
}

void* pthread_tcp(void* arg)
{
    printf("线程%u正在运行\n", (int)pthread_self());
    struct sockaddr_in addr_client;
    socklen_t len = sizeof(addr_client);
    bzero(&addr_client, sizeof(addr_client));

    int fd = accept(socketfd_tcp, (struct sockaddr*)&addr_client, &len);


    pthread_t  pid2;
    pthread_create(&pid2, NULL, pthread_tcp, NULL);

    char ip[20] = { 0 };
    unsigned short port;
    inet_ntop(AF_INET, &addr_client.sin_addr, ip, len);
    port = ntohs(addr_client.sin_port); 
    printf("%s：%hu已连接\n", ip, port);

    sock5_license(&addr_client, len, fd);
    printf("线程%u退出\n", (int)pthread_self());
    return NULL;

}


int main(void)
{
    tcp_creat();
    printf("等待连接\n");
    while (1)
    {
        printf("主线程%u正在运行\n", (int)pthread_self());
        pthread_tcp(NULL);
    }

}
